namespace game {
    import getTimer = egret.getTimer;
    import getQualifiedClassName = egret.getQualifiedClassName;

    export class TimerProxy {
        private static _instance: TimerProxy;
        private _timeVOArray: TimerVO[] = [];
        private _init: boolean = false;
        private _intervalId: number = -1;

        public static get instance(): TimerProxy {
            if (this._instance == null) this._instance = new TimerProxy();
            return this._instance;
        }

        public constructor() {
            // if(GameQuickTest.ctrlDown)
            //     KeyboardUtil.getInstance().addKeyUpListener(this.onKeyUp,this);
        }

        // private onKeyUp(e){
        //     if(e.keyCode == Key.T)
        //         this.log();
        // }

        public init(): void {
            this._init = true;
            this._intervalId = setInterval(() => {
                let len: number = this._timeVOArray.length;
                let hasNull: boolean = false;
                let currTime: number = getTimer();
                let timerVO: TimerVO;
                for (let i: number = 0; i < len; i++) {
                    timerVO = this._timeVOArray[i];
                    if (timerVO && !timerVO.isStop) {
                        //setTimeOut
                        if (timerVO.endTime && currTime >= timerVO.endTime) {
                            this._timeVOArray[i] = null;
                            hasNull = true;
                            timerVO.fun.apply(timerVO.thisObj, timerVO.args);
                            if (timerVO.finishCall) {
                                timerVO.finishCall.apply(timerVO.thisObj);
                            }
                        }
                        else if (timerVO.tickEndTime) {
                            while (currTime >= timerVO.tickEndTime && (!timerVO.disposed)) {
                                let callArgs = timerVO.args;
                                if (timerVO.showAdvanced) {
                                    callArgs[0] = currTime - timerVO.callerTime;
                                }
                                timerVO.fun.apply(timerVO.thisObj, callArgs);
                                timerVO.tickEndTime += timerVO.perTime;
                                timerVO.callerTime = currTime;
                            }
                            if (timerVO.disposed) hasNull = true;
                        }
                    }
                }
                if (hasNull) {
                    this._timeVOArray = this._timeVOArray.filter(this.filterFun);
                    if (this._timeVOArray.length == 0 && this._intervalId >= 0) {
                        clearInterval(this._intervalId);
                        this._init = false;
                        this._intervalId = -1
                    }
                }
            }, 30);
        }

        private filterFun(obj: TimerVO, index: number, arr: any): any {
            return obj != null;
        }


        /**
         * 嫁接 DisplayObject 的 ENTER_FRAME 事件
         * 除了特殊需求，如FPS检查，其余地方一律不允许使用每帧事件，而是使用addToEnterFrame方法
         */
        private _enterFrameList: Map<any, Map<Function, TimerVO>>;

        public addToEnterFrame(func: Function, callObj: any) {
            if (!this._enterFrameList) {
                this._enterFrameList = new Map<any, Map<Function, TimerVO>>();
            }

            if (!this._enterFrameList.has(callObj)) {
                let map = new Map<Function, TimerVO>();
                this._enterFrameList.set(callObj, map);
            }

            if (!this._enterFrameList.get(callObj).has(func)) {
                let timerVO = this.setInterval(func, Config.FRAME_RATE, 0, callObj);
                this._enterFrameList.get(callObj).set(func, timerVO);
            }
        }

        public removeFromEnterFrame(func: Function, callObj: any) {
            if (!this._enterFrameList) return;
            if (!this._enterFrameList.has(callObj)) return;
            if (!this._enterFrameList.get(callObj).has(func)) return;
            let timerVO = this._enterFrameList.get(callObj).get(func);
            this.clearTimeVO(timerVO);
            this._enterFrameList.get(callObj).delete(func);
        }

        /**
         * milSec
         */
        public setTimeout(callBack: Function, milSec: number, thisObj: any, ...args): TimerVO {
            if (args && (args.length == 0 || args.length == 1 && args[0] == null)) args = null;
            if (milSec == 0) {
                if (args)
                    callBack.apply(callBack, args);
                else
                    callBack();
            }
            else {
                if (!this._init) this.init();
                let t: number = getTimer();
                let timerVO: TimerVO = new TimerVO();
                timerVO.endTime = t + milSec;
                timerVO.fun = callBack;
                timerVO.args = args;
                timerVO.thisObj = thisObj;
                this._timeVOArray.push(timerVO);
                return timerVO;
            }
            return null;
        }

        public setInterval(callBack: Function, perTime: number, duringTime: number = 0, thisObj: any = null, ...args): TimerVO {
            if (!this._init) this.init();
            let timerVO: TimerVO = new TimerVO();
            let t: number = getTimer();
            timerVO.callerTime = t;
            timerVO.tickEndTime = t + perTime;
            timerVO.perTime = perTime;
            timerVO.endTime = duringTime ? t + duringTime : 0;
            timerVO.fun = callBack;
            timerVO.args = args;
            timerVO.thisObj = thisObj;
            if (callBack.length > args.length) {
                timerVO.showAdvanced = true;
                timerVO.args = args.concat();
                timerVO.args.unshift(0);
            } else {
                timerVO.showAdvanced = false;
            }
            this._timeVOArray.push(timerVO);
            return timerVO;
        }

        public clearTimeVO(vo: TimerVO): void {
            let index: number = this._timeVOArray.indexOf(vo);
            vo.disposed = true;
            if (index != -1) {
                this._timeVOArray[index] = null;
                this._timeVOArray = this._timeVOArray.filter(this.filterFun);
                if (this._timeVOArray.length == 0 && this._intervalId >= 0) {
                    clearInterval(this._intervalId);
                    this._intervalId = -1;
                    this._init = false;
                }
            }
        }

        public log(): void {
            let logData = "";
            let intervalList = [];
            let timeoutList = [];
            let len = this._timeVOArray.length;
            for (let i: number = 0; i < len; i++) {
                let timerVO = this._timeVOArray[i];
                if (timerVO && !timerVO.isStop) {
                    //setTimeOut
                    if (timerVO.endTime) {
                        timeoutList.push(getQualifiedClassName(timerVO.thisObj));
                    }
                    else if (timerVO.tickEndTime) {
                        intervalList.push(getQualifiedClassName(timerVO.thisObj));
                    }
                }
            }

            logData += "[Interval] :";
            for (let sign of intervalList)
                logData += sign + ' ';
            logData += "\n\n[Timeout] :";
            for (let sign of timeoutList)
                logData += sign + ' ';
            logData += '\n';
            console.log(logData);
        }
    }

    export class TimerVO {
        public endTime: number;
        public perTime: number;
        public tickEndTime: number;
        public callerTime: number;
        public args: any[];
        public fun: Function;
        public thisObj: any;
        public showAdvanced: boolean = false;
        public finishCall: Function;
        public disposed: boolean = false;

        private _stopStartTime: number;
        private _isStop: boolean = false;
        public get isStop() {
            return this._isStop;
        }

        public set isStop(v) {
            this._isStop = v;
            if (v) {
                if (!this.endTime) return;
                if (!this._stopStartTime)
                    this._stopStartTime = getTimer();
            }
            else {
                if (!this.endTime) return;
                if (this._stopStartTime)
                    this.endTime += getTimer() - this._stopStartTime;
                this._stopStartTime = undefined;
            }
        }
    }
}